/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace DaveSexton.DocProject
{
	public delegate void BuildStepExecuteCallback(BuildContext context);
	public delegate void Callback();

	public abstract class BuildProcessComponent : MarshalByRefObject
	{
		#region Private / Protected
		private BuildProcessComponentProxy marshaler;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="BuildProcessComponent" /> class for derived classes.
		/// </summary>
		protected BuildProcessComponent()
		{
		}
		#endregion

		#region methods
		[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand,
			Flags = System.Security.Permissions.SecurityPermissionFlag.Infrastructure)]
		public override object InitializeLifetimeService()
		{
			return null;
		}

		internal void Initialize(BuildProcessComponentProxy marshalerProxy)
		{
			if (marshalerProxy == null)
				throw new ArgumentNullException("marshalerProxy");

			marshaler = marshalerProxy;
		}

		public abstract void BuildStarting(BuildContext context);

		public abstract bool BeforeExecuteStep(DaveSexton.DocProject.Engine.IBuildStep step, BuildContext context);

		public abstract void AfterExecuteStep(DaveSexton.DocProject.Engine.IBuildStep step, BuildContext context);

		public abstract void BuildCompleted(BuildContext context);

		protected static Delegate CreateDelegateProxy(Delegate target)
		{
			return new RemoteDelegateProxy(target).Substitute;
		}

		protected void InvokeAsync(Callback target)
		{
			BuildContext context = marshaler.Context;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				context.InvokeAsync(CreateDelegateProxy(target), null);
			else
				context.InvokeAsync(target, null);
		}

		protected void InvokeAsync(Delegate target, params object[] arguments)
		{
			BuildContext context = marshaler.Context;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				target = CreateDelegateProxy(target);

			context.InvokeAsync(target, arguments);
		}

		/// <remarks>
		/// The specified <paramref name="target"/> will be invoked asynchronously if it doesn't return a value and if the 
		/// call is coming from the UI thread's <see cref="System.Threading.SynchronizationContext"/>.  A synchronous invocation
		/// will occur in all other scenarios.
		/// <para>To force asynchronous invocation, use one of the <see cref="InvokeAsync"/> methods instead.</para>
		/// </remarks>
		protected void Invoke(Callback target)
		{
			BuildContext context = marshaler.Context;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				context.Invoke(CreateDelegateProxy(target), null);
			else
				context.Invoke(target, null);
		}

		/// <remarks>
		/// The specified <paramref name="target"/> will be invoked asynchronously if it doesn't return a value and if the 
		/// call is coming from the UI thread's <see cref="System.Threading.SynchronizationContext"/>.  A synchronous invocation
		/// will occur in all other scenarios.
		/// <para>To force asynchronous invocation, use one of the <see cref="InvokeAsync"/> methods instead.</para>
		/// </remarks>
		protected object Invoke(Delegate target, params object[] arguments)
		{
			BuildContext context = marshaler.Context;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				target = CreateDelegateProxy(target);

			return context.Invoke(target, arguments);
		}

		public void AddBuildStep(string name, bool runInBackground, bool continueOnError, bool includeInPartialBuild,
			BuildStepExecuteCallback dynamicStep)
		{
			BuildContext context = marshaler.Context;
			BuildStepExecuteCallback callback;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				// NOTE: The returned value can be cast to BuildStepExecuteCallback because it's handled as a special case.
				callback = (BuildStepExecuteCallback) CreateDelegateProxy(dynamicStep);
			else
				callback = dynamicStep;

			context.Steps.Add(new DaveSexton.DocProject.Engine.DynamicBuildStep(context.Engine, callback,
				name, runInBackground, continueOnError, includeInPartialBuild));
		}

		public void InsertBuildStep(int index, string name, bool runInBackground, bool continueOnError, bool includeInPartialBuild,
			BuildStepExecuteCallback dynamicStep)
		{
			BuildContext context = marshaler.Context;
			BuildStepExecuteCallback callback;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				// NOTE: The returned value can be cast to BuildStepExecuteCallback because it's handled as a special case.
				callback = (BuildStepExecuteCallback) CreateDelegateProxy(dynamicStep);
			else
				callback = dynamicStep;

			context.Steps.Insert(index, new DaveSexton.DocProject.Engine.DynamicBuildStep(context.Engine, callback,
				name, runInBackground, continueOnError, includeInPartialBuild));
		}

		public void InsertBeforeBuildStep(string insertBeforeName, string name, bool runInBackground, bool continueOnError, bool includeInPartialBuild,
			BuildStepExecuteCallback dynamicStep)
		{
			BuildContext context = marshaler.Context;
			BuildStepExecuteCallback callback;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				// NOTE: The returned value can be cast to BuildStepExecuteCallback because it's handled as a special case.
				callback = (BuildStepExecuteCallback) CreateDelegateProxy(dynamicStep);
			else
				callback = dynamicStep;

			DaveSexton.DocProject.Engine.BuildStepCollection steps = context.Steps;

			steps.Insert(steps.IndexOf(insertBeforeName), new DaveSexton.DocProject.Engine.DynamicBuildStep(context.Engine, callback,
				name, runInBackground, continueOnError, includeInPartialBuild));
		}

		public void InsertAfterBuildStep(string insertAfterName, string name, bool runInBackground, bool continueOnError, bool includeInPartialBuild,
			BuildStepExecuteCallback dynamicStep)
		{
			BuildContext context = marshaler.Context;
			BuildStepExecuteCallback callback;

			if (System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(context))
				// NOTE: The returned value can be cast to BuildStepExecuteCallback because it's handled as a special case.
				callback = (BuildStepExecuteCallback) CreateDelegateProxy(dynamicStep);
			else
				callback = dynamicStep;

			DaveSexton.DocProject.Engine.BuildStepCollection steps = context.Steps;

			steps.Insert(steps.IndexOf(insertAfterName) + 1, new DaveSexton.DocProject.Engine.DynamicBuildStep(context.Engine, callback,
				name, runInBackground, continueOnError, includeInPartialBuild));
		}

		protected void Trace(string message)
		{
			marshaler.Context.Trace(message);
		}

		protected void Trace(string format, params object[] args)
		{
			marshaler.Context.Trace(format, args);
		}

		protected void TracePath(string path)
		{
			marshaler.Context.TracePath(path);
		}

		protected void TracePathLine(string path)
		{
			marshaler.Context.TracePathLine(path);
		}

		protected void TraceLine()
		{
			marshaler.Context.TraceLine();
		}

		protected void TraceLine(string message)
		{
			marshaler.Context.TraceLine(message);
		}

		protected void TraceLine(string format, params object[] args)
		{
			marshaler.Context.TraceLine(format, args);
		}

		protected DialogResult ShowDialog(Form form)
		{
			if (form == null)
				throw new ArgumentNullException("form");

			return marshaler.ShowDialogInternal(form);
		}

		protected void ShowChild(Form form)
		{
			if (form == null)
				throw new ArgumentNullException("form");

			marshaler.ShowChildInternal(form);
		}
		#endregion
	}
}
